package control;

import iterativo.gals.LexicalError;
import iterativo.gals.Lexico;
import iterativo.gals.SemanticError;
import iterativo.gals.SemanticoIterativo;
import iterativo.gals.Sintatico;
import iterativo.gals.SyntaticError;
import iterativo.gals.Token;

import java.util.ArrayList;
import java.util.List;

public class ControleIterativo {

    private List<NotificaTela> listeners = new ArrayList<NotificaTela>();
    private Lexico lexico = new Lexico();
    private Sintatico sintatico = new Sintatico();
    private SemanticoIterativo semantico = new SemanticoIterativo();
    private int numeroLinha = 1;
    private String conteudo;

    public void setListener(NotificaTela listenerObj) {
	listeners.add(listenerObj);
    }

    public void removeListener(NotificaTela listenerObj) {
	listeners.remove(listenerObj);
    }

    private void notificarListenersSucesso(String analise) {
	for (NotificaTela list : listeners) {
	    list.saidaConsoleSucesso(analise);
	}
    }

    private void notificarListenersErro(String analise) {
	for (NotificaTela list : listeners) {
	    list.saidaConsoleErro(analise);
	}
    }

    private void notificarListenersLimpaConsole() {
	for (NotificaTela list : listeners) {
	    list.limpaTxConsole();
	}
    }

    private char buscaSimboloInvalido(int position) {
	return conteudo.charAt(position);
    }

    private void buscaLinha(int position) {

	for (int i = 0; i <= position; i++) {

	    if (conteudo.length() == i || conteudo.length() < i) {
		return;
	    }

	    if (conteudo.charAt(i) == '\n' && i == position) {
		return;
	    } else if (conteudo.charAt(i) == '\n') {
		numeroLinha++;
	    }
	}
    }

    private void resetaLinha() {
	numeroLinha = 1;
    }

    public void setConteudo(String conteudo) {
	this.conteudo = conteudo;
    }

    public SemanticoIterativo getSemantico() {
	return semantico;
    }

    public void analisar() {

	lexico.setInput(conteudo);

	semantico = new SemanticoIterativo();

	notificarListenersLimpaConsole();

	try {

	    sintatico.parse(lexico, semantico);

	    notificarListenersSucesso("\t\tPrograma compilado com sucesso!");

	} catch (LexicalError e) {

	    buscaLinha(e.getPosition());

	    if (e.getMessage().equals("símbolo inválido")) {

		char invalid = buscaSimboloInvalido(e.getPosition());

		notificarListenersErro("Erro na linha " + numeroLinha + " - " + invalid + " " + e.getMessage());

	    } else {

		notificarListenersErro("Erro na linha " + numeroLinha + " - " + e.getMessage());
	    }

	    resetaLinha();

	} catch (SyntaticError e) {

	    buscaLinha(e.getPosition());

	    String tipo = determinaTipo(sintatico.getCurrentToken());

	    notificarListenersErro("Erro sintático na linha  " + numeroLinha + ", " + " encontrado " + tipo + " " + sintatico.getCurrentTokenString() + ", " + e.getMessage());

	    resetaLinha();

	} catch (SemanticError e) {
	    buscaLinha(e.getPosition());
	    notificarListenersErro("Erro na linha " + numeroLinha + " - " + e.getMessage());
	    resetaLinha();
	}
    }

    public List<String> gerarCodigo() {

	int numeroEnder = semantico.getIterativa().getC0().size();

	List<String> codigoMonolitico = new ArrayList<String>();

	codigoMonolitico.add(semantico.construirHeader());

	for (int i = 0; i < numeroEnder; i++) {

	    Integer c1 = semantico.getIterativa().getC1().get(i + 1);
	    Integer c2 = semantico.getIterativa().getC2().get(i + 1);
	    Integer c3 = semantico.getIterativa().getC3().get(i + 1);
	    Integer c4 = semantico.getIterativa().getC4().get(i + 1);
	    Integer c5 = semantico.getIterativa().getC5().get(i + 1);

	    // System.out.println(semantico.getIterativa().getC0().get(i) + " " + c1 + " " + c2 +
	    // " " + c3 + " " + c4 + " " + c5);
	    codigoMonolitico.add(transformarEmMonolitico(i + 1, c1, c2, c3, c4, c5));
	}

	List<Integer> c0 = semantico.getIterativa().getC0();
	codigoMonolitico.add(c0.size() + 1 + ": retorna");
	for (String s : codigoMonolitico) {
	    System.out.println(s);
	}
	return codigoMonolitico;
    }

    private static String transformarEmMonolitico(int indice, Integer c1,
	Integer c2, Integer c3, Integer c4, Integer c5) {

	if (c1 == 0) {
	    return operacaoVazia(indice, c1, c4);
	}
	if (c1 == 1) {
	    return operacaoTeste(indice, c1, c4, c5);

	}
	if (c1 == 2) {
	    return operacaoAtribuicaoRegistrador(indice, c1, c3, c4);

	}
	if (c1 == 3) {
	    return operacaoIncremento(indice, c2, c4);

	} else {
	    return operacaoDecremento(indice, c2, c4);
	}
    }

    private static String operacaoVazia(int indice, Integer c1, Integer c4) {
	return indice + ": faca rt = 0 va_para " + c4;
    }

    private static String operacaoTeste(int indice, Integer c1, Integer c4,
	Integer c5) {

	return indice + ": se T entao va_para " + c4 + " senao va_para " + c5;
    }

    private static String operacaoAtribuicaoRegistrador(int indice, Integer c1,
	Integer c3, Integer c4) {

	return indice + ": faca rt = r" + c3 + " va_para " + c4;
    }

    private static String operacaoIncremento(int indice, Integer c2, Integer c4) {

	return indice + ": faca inc(r" + c2 + ") va_para " + c4;
    }

    private static String operacaoDecremento(int indice, Integer c2, Integer c4) {

	return indice + ": faca dec(r" + c2 + ") va_para " + c4;
    }

    private String determinaTipo(Token currentToken) {

	switch (currentToken.getId()) {

	case 2:
	    return "identificador";

	case 3:
	    return "operação";

	case 4:
	    return "palavra reservada invalida";

	default:

	    if (currentToken.getId() > 4 && currentToken.getId() < 12) {
		return "palavra reservada";
	    }

	    if (currentToken.getId() > 11) {
		return "símbolo especial";
	    }
	}
	return "";

    }

    public String getNomePrograma() {
	return semantico.getNomePrograma();
    }

}
